
// 属性转换器集合
var propertyConvertors = $create(null);


// 当前绑定目标
var bindingTarget;




propertyConvertors.boolean = function (value) {
    
    return !!value && value !== 'false';
}


propertyConvertors.integer = function (value) {

    return value | 0;
}


propertyConvertors.number = function (value) {

    return +value || 0;
}


propertyConvertors.string = function (value) {

    return '' + value;
}



// 重写以解决webpack打包的static properties无法执行set的问题
Object.defineProperty = function (object, key, descriptor) {

    if (key === 'properties' && object.prototype && object.prototype.__properties)
    {
        object[key] = descriptor.value;
    }
    else
    {
        $define(object, key, descriptor);
    }
}



// 添加订阅
function appendDependences(provider, name, subscriber) {

    var dependences, list;

    // 添加观察对象
    if (dependences = provider.__deps)
    {
        if (list = dependences[name])
        {
            list.push(subscriber);
            return list;
        }

        return dependences[name] = [subscriber];
    }

    return (provider.__deps = {})[name] = [subscriber];
}


// 通知订阅
function notifyDependences(dependences, name) {

    var list, subscriber, index;

    if (name && (list = dependences[name]))
    {
        index = 0;

        while (subscriber = list[index++])
        {
            subscriber.onchange(name);
        }
    }

    if (list = dependences['*'])
    {
        index = 0;

        while (subscriber = list[index++])
        {
            subscriber.onchange(name);
        }
    }
}


// 取消所有依赖
function removeDependences(provider) {

    var dependences = provider.__deps;
    var list, subscriber, bindings;

    for (var name in dependences)
    {
        if (list = dependences[name])
        {
            dependences[name] = null;

            for (var i = list.length; i--;)
            {
                if ((subscriber = list[i]) && 
                    (subscriber = subscriber.target) &&
                    (bindings = subscriber && subscriber.__bindings))
                {
                    for (var j = bindings.length; j--;)
                    {
                        if (bindings[j][0] === provider)
                        {
                            bindings.splice(j, 1);
                        }
                    }

                    if (bindings.length <= 0)
                    {
                        subscriber.__bindings = null;
                    }
                }
            }
        }
    }
}



// 订阅绑定
function subscribeBinding(provider, name) {

    var subscriber = bindingTarget;
    var dependences = appendDependences(provider, name, subscriber);
    var binding = [provider, name, dependences, subscriber];

    // 订阅目标对象
    subscriber = subscriber.target;

    // 给对象记录依赖关系以便对象销毁时自动解除绑定
    if (dependences = subscriber.__bindings)
    {
        dependences.push(binding);
    }
    else
    {
        subscriber.__bindings = [binding];
    }
}


// 同步绑定
function synchronizeBind() {

    var target = this.target;
    target[this.property] = this.fn(target, false);
}


// 绑定
function bindProperty(subscriber, propertyName, bindingFn, onchange) {

    try
    {
        // 属性绑定
        if (typeof bindingFn !== 'function')
        {
            throwTextError('bind error: argument bindingFn is not a function!');
        }

        bindingTarget = {
            target: subscriber,
            property: propertyName,
            fn: bindingFn,
            onchange: onchange || synchronizeBind
        };

        if (onchange)
        {
            onchange.call(bindingTarget, propertyName, true);
        }
        else
        {
            subscriber[propertyName] = bindingFn(subscriber, true);
        }
    }
    finally
    {
        bindingTarget = null;
    }
}


// 解除所有绑定
function unbindProperty(bindings) {

    for (var i = bindings.length; i--;)
    {
        var binding = bindings[i];
        var dependences = binding[2];

        for (var j = dependences.length; j--;)
        {
            if (dependences[j] === binding[3])
            {
                dependences.splice(j, 1);

                if (dependences.length <= 0)
                {
                    binding[0].__deps[binding[1]] = null;
                }
                break;
            }
        }
    }
}



// 循环处理properties以解决使用es6的class写法时可能缺少基类属性集的问题
// 初始化类型的properties
// 每个类型都要有独立的properties以免冲突
function initProperties(provider) {

    var base, defaults;

    if (base = Object.getPrototypeOf(provider))
    {
        if (base.hasOwnProperty('__defaults'))
        {
            defaults = base.__defaults;
        }
        else
        {
            defaults = initProperties(base);
        }

        provider.__properties = $create(base.__properties || null);
        return provider.__defaults = $create(defaults || null);
    }
}


function buildPropertyGet(name, descriptor) {
    
    var get = descriptor.get;

    return function () {

        if (bindingTarget)
        {
            subscribeBinding(this, name);
        }

        return get.call(this);
    }
}


// 指定了set时change无效
function buildPropertySet(name, descriptor) {

    var $notify = notifyDependences;
    var set = descriptor.set;
    var notify = descriptor.notify;

    return function (value) {

        if (set.call(this, value) !== false)
        {
            var any;

            // 触发定制通知
            if (any = notify)
            {
                this[any](name, value, descriptor);
            }

            // 触发订阅通知
            if ((any = this.__deps) && (any[name] || any['*']))
            {
                $notify(any, name);
            }
        }
    }
}


function buildDefaultPropertyGet(name, descriptor) {

    return function () {

        if (bindingTarget)
        {
            subscribeBinding(this, name);
        }

        return this.__fields[name];
    };
}


function buildDefaultPropertySet(name, descriptor) {

    var $notify = notifyDependences;
    var convert = descriptor.convert;
    var onchange = descriptor.onchange;
    var notify = descriptor.notify;

    return function (value) {

        var fields = this.__fields;
        var any;

        if (any = convert)
        {
            value = any(value);
        }

        if (value !== (any = fields[name]))
        {
            // 更新值
            fields[name] = value;

            // 自定义变更处理
            if (onchange && onchange.call(this, value, any) === false)
            {
                // 回退
                fields[name] = any;
                return false;
            }

            // 触发定制通知
            if (any = notify)
            {
                this[any](name, value, descriptor);
            }

            // 触发订阅通知
            if ((any = this.__deps) && (any[name] || any['*']))
            {
                $notify(any, name);
            }
        }
    }
}



// 扩展属性集支持
function extendProperties(Class, propertiesFn) {

    var prototype = Class.prototype;

    // 属性集
    prototype.__properties = $create(null);

    // 默认值
    prototype.__defaults = $create(null);

    // 静态属性集定义
    $define(Class, 'properties', prototype.__static_properties = {

        get: function () {

            return this.prototype.__properties;
        },

        set: propertiesFn || defineProperties
    });
}


// 定义属性
function defineProperty(provider, name, descriptor) {

    var defaultValue = descriptor.defaultValue;

    if (defaultValue === void 0)
    {
        defaultValue = descriptor.defaultValue = null;
    }

    if (provider.hasOwnProperty('__defaults'))
    {
        provider.__defaults[name] = defaultValue;
    }
    else
    {
        initProperties(provider)[name] = defaultValue;
    }

    descriptor.name = name;
    descriptor.type || (descriptor.type = typeof defaultValue);

    // 指定了get如果需要支持set则必须自己实现
    if (descriptor.get)
    {
        if (descriptor.set)
        {
            descriptor.get = buildPropertyGet(name, descriptor);
            descriptor.set = buildPropertySet(name, descriptor);
        }
        else // 没有设置set则表示只读属性 此时不收集依赖
        {
            descriptor.set = function () {

                var type = provider.typeName;
                throwTextError('property "' + name + '"' + (type ? ' of ' + type : '') + ' is readonly!');
            };
        }
    }
    else
    {
        if (descriptor.convert === void 0)
        {
            descriptor.convert = propertyConvertors[descriptor.type] || null;
        }

        descriptor.get = buildDefaultPropertyGet(name, descriptor);
        descriptor.set = (descriptor.set ? buildPropertySet : buildDefaultPropertySet)(name, descriptor);
    }

    // 定义属性
    $define(provider, name, provider.__properties[name] = descriptor);
}


// 默认定义属性集实现
function defineProperties(properties) {

    var prototype = this.prototype;
    var descriptor;

    for (var name in properties)
    {
        if (descriptor = properties[name])
        {
            switch (typeof descriptor)
            {
                case 'object':
                    break;

                case 'function':
                    descriptor = { 
                        defaultValue: null, 
                        get: descriptor
                    };
                    break;

                default:
                    descriptor = { 
                        defaultValue: descriptor
                    };
                    break;
            }
        }
        else
        {
            descriptor = {
                defaultValue: descriptor == null ? null : descriptor
            };
        }

        // 定义属性
        defineProperty(prototype, name, descriptor);
    }
}


// 修改默认值
function changeDefaultValue(provider, name, value) {

    if (!provider.hasOwnProperty('__defaults'))
    {
        initProperties(provider);
    }

    provider.__defaults[name] = value;
}


// 加载属性集的默认方法(内部用)
function loadProperties(target, properties) {

    for (var name in properties)
    {
        var value = properties[name];

        switch (name)
        {
            case 'bindings':
                for (var key in value)
                {
                    bindProperty(target, key, value[key]);
                }
                break;

            case 'events':
                for (var key in value)
                {
                    target.on(key, value[key]);
                }
                break;

            case 'ref':
                value(target);
                break;

            default:
                target[name] = value;
                break;
        }
    }
}



function notifyWatch() {

    var provider = this.provider;
    var name = this.name;

    this.fn(name !== '*' ? provider[name] : provider);
}


// 观测属性变更
function watchProperty(provider, name, watchFn) {

    if (typeof watchFn !== 'function')
    {
        throwTextError('call method watch error: watchFn argument must be a function!');
    }

    var watch = provider.__watch;

    var subscriber = {
        provider: provider,
        name: name,
        fn: watchFn,
        $onchange: notifyWatch
    };

    return [
        fn ? watch(provider, subscriber) : appendDependences(provider, name, subscriber),
        subscriber
    ];
}


// 取消属性变更观测
function unwatchProperty(watchResult) {

    var dependences, subscriber, provider, unwatch;

    if ((dependences = watchResult[0]) && (subscriber = watchResult[1]))
    {
        provider = subscriber.provider;

        if (unwatch = provider.__unwatch)
        {
            unwatch(provider, dependences, subscriber);
        }
        else
        {
            for (var i = dependences.length; i--;)
            {
                if (dependences[i] === subscriber)
                {
                    dependences.splice(i, 1);

                    if (dependences.length <= 0)
                    {
                        provider.__deps[subscriber.name] = null;
                    }

                    break;
                }
            }
        }
    }
}




// 属性集装饰器(装饰器@p)
// Object.propertiesDecorator = function (Class, properties) {

//     var prototype = Class.prototype || Class;
//     var fn, prototype, descriptor;

//     // 类自身定义了创建属性集的方法(如: ObjectModel)
//     if (prototype.__properties)
//     {
//         Class.properties = properties;
//     }
//     else
//     {
//         fn = $define;

//         for (var name in properties)
//         {
//             descriptor = properties[name];
            
//             if (!descriptor || typeof descriptor !== 'object')
//             {
//                 descriptor = { value: descriptor };
//             }

//             fn(prototype, name, descriptor);
//         }
//     }

//     return Class;
// }
